/*
 * 11/19/04 1.0 moved to LGPL.
 * 
 * 04/01/00 Fixes for running under build 23xx Microsoft JVM. mdm.
 * 
 * 19/12/99 Performance improvements to compute_pcm_samples().  
 * Mat McGowan. mdm@techie.com. 
 *
 * 16/02/99 Java Conversion by E.B , javalayer@javazoom.net
 *
 *  @(#) synthesis_filter.h 1.8, last edit: 6/15/94 16:52:00
 *  @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)
 *  @(#) Berlin University of Technology
 *
 *-----------------------------------------------------------------------
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as published
 *   by the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *----------------------------------------------------------------------
 */
package org.mp3transform;

/**
 * A class for the synthesis filter bank. This class does a fast downsampling from 32, 44.1 or 48 kHz to 8 kHz, if ULAW
 * is defined. Frequencies above 4 kHz are removed by ignoring higher subbands.
 */
final class SynthesisFilter {
    private double[] v1 = new double[512];
    private double[] v2 = new double[512];
    private double[] actualV = v1; // v1 or v2
    private int actualWritePos = 15; // 0-15
    private double[] samples = new double[32]; // 32 new subband samples
    private int channel;
    private double scaleFactor;
    private double[] tmpOutBuffer = new double[32];
    // DOUBLE
    private static final double MY_PI = 3.14159265358979323846;
    private static final double COS1_64 = divCos(MY_PI / 64.0);
    private static final double COS3_64 = divCos(MY_PI * 3.0 / 64.0);
    private static final double COS5_64 = divCos(MY_PI * 5.0 / 64.0);
    private static final double COS7_64 = divCos(MY_PI * 7.0 / 64.0);
    private static final double COS9_64 = divCos(MY_PI * 9.0 / 64.0);
    private static final double COS11_64 = divCos(MY_PI * 11.0 / 64.0);
    private static final double COS13_64 = divCos(MY_PI * 13.0 / 64.0);
    private static final double COS15_64 = divCos(MY_PI * 15.0 / 64.0);
    private static final double COS17_64 = divCos(MY_PI * 17.0 / 64.0);
    private static final double COS19_64 = divCos(MY_PI * 19.0 / 64.0);
    private static final double COS21_64 = divCos(MY_PI * 21.0 / 64.0);
    private static final double COS23_64 = divCos(MY_PI * 23.0 / 64.0);
    private static final double COS25_64 = divCos(MY_PI * 25.0 / 64.0);
    private static final double COS27_64 = divCos(MY_PI * 27.0 / 64.0);
    private static final double COS29_64 = divCos(MY_PI * 29.0 / 64.0);
    private static final double COS31_64 = divCos(MY_PI * 31.0 / 64.0);
    private static final double COS1_32 = divCos(MY_PI / 32.0);
    private static final double COS3_32 = divCos(MY_PI * 3.0 / 32.0);
    private static final double COS5_32 = divCos(MY_PI * 5.0 / 32.0);
    private static final double COS7_32 = divCos(MY_PI * 7.0 / 32.0);
    private static final double COS9_32 = divCos(MY_PI * 9.0 / 32.0);
    private static final double COS11_32 = divCos(MY_PI * 11.0 / 32.0);
    private static final double COS13_32 = divCos(MY_PI * 13.0 / 32.0);
    private static final double COS15_32 = divCos(MY_PI * 15.0 / 32.0);
    private static final double COS1_16 = divCos(MY_PI / 16.0);
    private static final double COS3_16 = divCos(MY_PI * 3.0 / 16.0);
    private static final double COS5_16 = divCos(MY_PI * 5.0 / 16.0);
    private static final double COS7_16 = divCos(MY_PI * 7.0 / 16.0);
    private static final double COS1_8 = divCos(MY_PI / 8.0);
    private static final double COS3_8 = divCos(MY_PI * 3.0 / 8.0);
    private static final double COS1_4 = divCos(MY_PI / 4.0);
    private static final double[][] D16 = Constants.D16;

    /**
     * Contructor. The scalefactor scales the calculated double pcm samples to short values (raw pcm samples are in
     * [-1.0, 1.0], if no violations occur).
     */
    SynthesisFilter(int channelNumber, double factor) {
        channel = channelNumber;
        scaleFactor = factor;
    }

    private static double divCos(double a) {
        return (1.0 / (2.0 * Math.cos(a)));
    }

    /**
     * Compute new values via a fast cosine transform.
     */
    private void computeNewV() {
        double nv0, nv1, nv2, nv3, nv4, nv5, nv6, nv7, nv8, nv9;
        double nv10, nv11, nv12, nv13, nv14, nv15, nv16, nv17, nv18, nv19;
        double nv20, nv21, nv22, nv23, nv24, nv25, nv26, nv27, nv28, nv29;
        double nv30, nv31;
        double[] s = samples;
        double s0 = s[0];
        double s1 = s[1];
        double s2 = s[2];
        double s3 = s[3];
        double s4 = s[4];
        double s5 = s[5];
        double s6 = s[6];
        double s7 = s[7];
        double s8 = s[8];
        double s9 = s[9];
        double s10 = s[10];
        double s11 = s[11];
        double s12 = s[12];
        double s13 = s[13];
        double s14 = s[14];
        double s15 = s[15];
        double s16 = s[16];
        double s17 = s[17];
        double s18 = s[18];
        double s19 = s[19];
        double s20 = s[20];
        double s21 = s[21];
        double s22 = s[22];
        double s23 = s[23];
        double s24 = s[24];
        double s25 = s[25];
        double s26 = s[26];
        double s27 = s[27];
        double s28 = s[28];
        double s29 = s[29];
        double s30 = s[30];
        double s31 = s[31];
        double p0 = s0 + s31;
        double p1 = s1 + s30;
        double p2 = s2 + s29;
        double p3 = s3 + s28;
        double p4 = s4 + s27;
        double p5 = s5 + s26;
        double p6 = s6 + s25;
        double p7 = s7 + s24;
        double p8 = s8 + s23;
        double p9 = s9 + s22;
        double p10 = s10 + s21;
        double p11 = s11 + s20;
        double p12 = s12 + s19;
        double p13 = s13 + s18;
        double p14 = s14 + s17;
        double p15 = s15 + s16;
        double pp0 = p0 + p15;
        double pp1 = p1 + p14;
        double pp2 = p2 + p13;
        double pp3 = p3 + p12;
        double pp4 = p4 + p11;
        double pp5 = p5 + p10;
        double pp6 = p6 + p9;
        double pp7 = p7 + p8;
        double pp8 = (p0 - p15) * COS1_32;
        double pp9 = (p1 - p14) * COS3_32;
        double pp10 = (p2 - p13) * COS5_32;
        double pp11 = (p3 - p12) * COS7_32;
        double pp12 = (p4 - p11) * COS9_32;
        double pp13 = (p5 - p10) * COS11_32;
        double pp14 = (p6 - p9) * COS13_32;
        double pp15 = (p7 - p8) * COS15_32;
        p0 = pp0 + pp7;
        p1 = pp1 + pp6;
        p2 = pp2 + pp5;
        p3 = pp3 + pp4;
        p4 = (pp0 - pp7) * COS1_16;
        p5 = (pp1 - pp6) * COS3_16;
        p6 = (pp2 - pp5) * COS5_16;
        p7 = (pp3 - pp4) * COS7_16;
        p8 = pp8 + pp15;
        p9 = pp9 + pp14;
        p10 = pp10 + pp13;
        p11 = pp11 + pp12;
        p12 = (pp8 - pp15) * COS1_16;
        p13 = (pp9 - pp14) * COS3_16;
        p14 = (pp10 - pp13) * COS5_16;
        p15 = (pp11 - pp12) * COS7_16;
        pp0 = p0 + p3;
        pp1 = p1 + p2;
        pp2 = (p0 - p3) * COS1_8;
        pp3 = (p1 - p2) * COS3_8;
        pp4 = p4 + p7;
        pp5 = p5 + p6;
        pp6 = (p4 - p7) * COS1_8;
        pp7 = (p5 - p6) * COS3_8;
        pp8 = p8 + p11;
        pp9 = p9 + p10;
        pp10 = (p8 - p11) * COS1_8;
        pp11 = (p9 - p10) * COS3_8;
        pp12 = p12 + p15;
        pp13 = p13 + p14;
        pp14 = (p12 - p15) * COS1_8;
        pp15 = (p13 - p14) * COS3_8;
        p0 = pp0 + pp1;
        p1 = (pp0 - pp1) * COS1_4;
        p2 = pp2 + pp3;
        p3 = (pp2 - pp3) * COS1_4;
        p4 = pp4 + pp5;
        p5 = (pp4 - pp5) * COS1_4;
        p6 = pp6 + pp7;
        p7 = (pp6 - pp7) * COS1_4;
        p8 = pp8 + pp9;
        p9 = (pp8 - pp9) * COS1_4;
        p10 = pp10 + pp11;
        p11 = (pp10 - pp11) * COS1_4;
        p12 = pp12 + pp13;
        p13 = (pp12 - pp13) * COS1_4;
        p14 = pp14 + pp15;
        p15 = (pp14 - pp15) * COS1_4;
        // this is pretty insane coding
        double tmp1;
        nv19 = -(nv4 = (nv12 = p7) + p5) - p6; // 36-17
        nv27 = -p6 - p7 - p4; // 44-17
        nv6 = (nv10 = (nv14 = p15) + p11) + p13;
        nv17 = -(nv2 = p15 + p13 + p9) - p14; // 34-17
        nv21 = (tmp1 = -p14 - p15 - p10 - p11) - p13; // 38-17
        nv29 = -p14 - p15 - p12 - p8; // 46-17
        nv25 = tmp1 - p12; // 42-17
        nv31 = -p0; // 48-17
        nv0 = p1;
        nv23 = -(nv8 = p3) - p2; // 40-17
        p0 = (s0 - s31) * COS1_64;
        p1 = (s1 - s30) * COS3_64;
        p2 = (s2 - s29) * COS5_64;
        p3 = (s3 - s28) * COS7_64;
        p4 = (s4 - s27) * COS9_64;
        p5 = (s5 - s26) * COS11_64;
        p6 = (s6 - s25) * COS13_64;
        p7 = (s7 - s24) * COS15_64;
        p8 = (s8 - s23) * COS17_64;
        p9 = (s9 - s22) * COS19_64;
        p10 = (s10 - s21) * COS21_64;
        p11 = (s11 - s20) * COS23_64;
        p12 = (s12 - s19) * COS25_64;
        p13 = (s13 - s18) * COS27_64;
        p14 = (s14 - s17) * COS29_64;
        p15 = (s15 - s16) * COS31_64;
        pp0 = p0 + p15;
        pp1 = p1 + p14;
        pp2 = p2 + p13;
        pp3 = p3 + p12;
        pp4 = p4 + p11;
        pp5 = p5 + p10;
        pp6 = p6 + p9;
        pp7 = p7 + p8;
        pp8 = (p0 - p15) * COS1_32;
        pp9 = (p1 - p14) * COS3_32;
        pp10 = (p2 - p13) * COS5_32;
        pp11 = (p3 - p12) * COS7_32;
        pp12 = (p4 - p11) * COS9_32;
        pp13 = (p5 - p10) * COS11_32;
        pp14 = (p6 - p9) * COS13_32;
        pp15 = (p7 - p8) * COS15_32;
        p0 = pp0 + pp7;
        p1 = pp1 + pp6;
        p2 = pp2 + pp5;
        p3 = pp3 + pp4;
        p4 = (pp0 - pp7) * COS1_16;
        p5 = (pp1 - pp6) * COS3_16;
        p6 = (pp2 - pp5) * COS5_16;
        p7 = (pp3 - pp4) * COS7_16;
        p8 = pp8 + pp15;
        p9 = pp9 + pp14;
        p10 = pp10 + pp13;
        p11 = pp11 + pp12;
        p12 = (pp8 - pp15) * COS1_16;
        p13 = (pp9 - pp14) * COS3_16;
        p14 = (pp10 - pp13) * COS5_16;
        p15 = (pp11 - pp12) * COS7_16;
        pp0 = p0 + p3;
        pp1 = p1 + p2;
        pp2 = (p0 - p3) * COS1_8;
        pp3 = (p1 - p2) * COS3_8;
        pp4 = p4 + p7;
        pp5 = p5 + p6;
        pp6 = (p4 - p7) * COS1_8;
        pp7 = (p5 - p6) * COS3_8;
        pp8 = p8 + p11;
        pp9 = p9 + p10;
        pp10 = (p8 - p11) * COS1_8;
        pp11 = (p9 - p10) * COS3_8;
        pp12 = p12 + p15;
        pp13 = p13 + p14;
        pp14 = (p12 - p15) * COS1_8;
        pp15 = (p13 - p14) * COS3_8;
        p0 = pp0 + pp1;
        p1 = (pp0 - pp1) * COS1_4;
        p2 = pp2 + pp3;
        p3 = (pp2 - pp3) * COS1_4;
        p4 = pp4 + pp5;
        p5 = (pp4 - pp5) * COS1_4;
        p6 = pp6 + pp7;
        p7 = (pp6 - pp7) * COS1_4;
        p8 = pp8 + pp9;
        p9 = (pp8 - pp9) * COS1_4;
        p10 = pp10 + pp11;
        p11 = (pp10 - pp11) * COS1_4;
        p12 = pp12 + pp13;
        p13 = (pp12 - pp13) * COS1_4;
        p14 = pp14 + pp15;
        p15 = (pp14 - pp15) * COS1_4;
        // manually doing something that a compiler should handle sucks
        // coding like this is hard to read
        double tmp2;
        nv5 = (nv11 = (nv13 = (nv15 = p15) + p7) + p11) + p5 + p13;
        nv7 = (nv9 = p15 + p11 + p3) + p13;
        nv16 = -(nv1 = (tmp1 = p13 + p15 + p9) + p1) - p14; // 33-17
        nv18 = -(nv3 = tmp1 + p5 + p7) - p6 - p14; // 35-17
        nv22 = (tmp1 = -p10 - p11 - p14 - p15) - p13 - p2 - p3; // 39-17
        nv20 = tmp1 - p13 - p5 - p6 - p7; // 37-17
        nv24 = tmp1 - p12 - p2 - p3; // 41-17
        nv26 = tmp1 - p12 - (tmp2 = p4 + p6 + p7); // 43-17
        nv30 = (tmp1 = -p8 - p12 - p14 - p15) - p0; // 47-17
        nv28 = tmp1 - tmp2; // 45-17
        // insert V[0-15] (== nv[0-15]) into actual v:
        // double[] x2 = actual_v + actual_write_pos;
        double[] dest = actualV;
        int pos = actualWritePos;
        dest[0 + pos] = nv0;
        dest[16 + pos] = nv1;
        dest[32 + pos] = nv2;
        dest[48 + pos] = nv3;
        dest[64 + pos] = nv4;
        dest[80 + pos] = nv5;
        dest[96 + pos] = nv6;
        dest[112 + pos] = nv7;
        dest[128 + pos] = nv8;
        dest[144 + pos] = nv9;
        dest[160 + pos] = nv10;
        dest[176 + pos] = nv11;
        dest[192 + pos] = nv12;
        dest[208 + pos] = nv13;
        dest[224 + pos] = nv14;
        dest[240 + pos] = nv15;
        // V[16] is always 0.0:
        dest[256 + pos] = 0.0f;
        // insert V[17-31] (== -nv[15-1]) into actual v:
        dest[272 + pos] = -nv15;
        dest[288 + pos] = -nv14;
        dest[304 + pos] = -nv13;
        dest[320 + pos] = -nv12;
        dest[336 + pos] = -nv11;
        dest[352 + pos] = -nv10;
        dest[368 + pos] = -nv9;
        dest[384 + pos] = -nv8;
        dest[400 + pos] = -nv7;
        dest[416 + pos] = -nv6;
        dest[432 + pos] = -nv5;
        dest[448 + pos] = -nv4;
        dest[464 + pos] = -nv3;
        dest[480 + pos] = -nv2;
        dest[496 + pos] = -nv1;
        // insert V[32] (== -nv[0]) into other v:
        dest = (actualV == v1) ? v2 : v1;
        dest[0 + pos] = -nv0;
        // insert V[33-48] (== nv[16-31]) into other v:
        dest[16 + pos] = nv16;
        dest[32 + pos] = nv17;
        dest[48 + pos] = nv18;
        dest[64 + pos] = nv19;
        dest[80 + pos] = nv20;
        dest[96 + pos] = nv21;
        dest[112 + pos] = nv22;
        dest[128 + pos] = nv23;
        dest[144 + pos] = nv24;
        dest[160 + pos] = nv25;
        dest[176 + pos] = nv26;
        dest[192 + pos] = nv27;
        dest[208 + pos] = nv28;
        dest[224 + pos] = nv29;
        dest[240 + pos] = nv30;
        dest[256 + pos] = nv31;
        // insert V[49-63] (== nv[30-16]) into other v:
        dest[272 + pos] = nv30;
        dest[288 + pos] = nv29;
        dest[304 + pos] = nv28;
        dest[320 + pos] = nv27;
        dest[336 + pos] = nv26;
        dest[352 + pos] = nv25;
        dest[368 + pos] = nv24;
        dest[384 + pos] = nv23;
        dest[400 + pos] = nv22;
        dest[416 + pos] = nv21;
        dest[432 + pos] = nv20;
        dest[448 + pos] = nv19;
        dest[464 + pos] = nv18;
        dest[480 + pos] = nv17;
        dest[496 + pos] = nv16;
    }

    private void computePcmSamples0() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[0 + dvp] * dp[0]) + (vp[15 + dvp] * dp[1]) + (vp[14 + dvp] * dp[2]) + (vp[13 + dvp] * dp[3]) + (vp[12 + dvp] * dp[4]) + (vp[11 + dvp] * dp[5])
                    + (vp[10 + dvp] * dp[6]) + (vp[9 + dvp] * dp[7]) + (vp[8 + dvp] * dp[8]) + (vp[7 + dvp] * dp[9]) + (vp[6 + dvp] * dp[10]) + (vp[5 + dvp] * dp[11])
                    + (vp[4 + dvp] * dp[12]) + (vp[3 + dvp] * dp[13]) + (vp[2 + dvp] * dp[14]) + (vp[1 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples1() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[1 + dvp] * dp[0]) + (vp[0 + dvp] * dp[1]) + (vp[15 + dvp] * dp[2]) + (vp[14 + dvp] * dp[3]) + (vp[13 + dvp] * dp[4]) + (vp[12 + dvp] * dp[5])
                    + (vp[11 + dvp] * dp[6]) + (vp[10 + dvp] * dp[7]) + (vp[9 + dvp] * dp[8]) + (vp[8 + dvp] * dp[9]) + (vp[7 + dvp] * dp[10]) + (vp[6 + dvp] * dp[11])
                    + (vp[5 + dvp] * dp[12]) + (vp[4 + dvp] * dp[13]) + (vp[3 + dvp] * dp[14]) + (vp[2 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples2() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[2 + dvp] * dp[0]) + (vp[1 + dvp] * dp[1]) + (vp[0 + dvp] * dp[2]) + (vp[15 + dvp] * dp[3]) + (vp[14 + dvp] * dp[4]) + (vp[13 + dvp] * dp[5])
                    + (vp[12 + dvp] * dp[6]) + (vp[11 + dvp] * dp[7]) + (vp[10 + dvp] * dp[8]) + (vp[9 + dvp] * dp[9]) + (vp[8 + dvp] * dp[10]) + (vp[7 + dvp] * dp[11])
                    + (vp[6 + dvp] * dp[12]) + (vp[5 + dvp] * dp[13]) + (vp[4 + dvp] * dp[14]) + (vp[3 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples3() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[3 + dvp] * dp[0]) + (vp[2 + dvp] * dp[1]) + (vp[1 + dvp] * dp[2]) + (vp[0 + dvp] * dp[3]) + (vp[15 + dvp] * dp[4]) + (vp[14 + dvp] * dp[5])
                    + (vp[13 + dvp] * dp[6]) + (vp[12 + dvp] * dp[7]) + (vp[11 + dvp] * dp[8]) + (vp[10 + dvp] * dp[9]) + (vp[9 + dvp] * dp[10]) + (vp[8 + dvp] * dp[11])
                    + (vp[7 + dvp] * dp[12]) + (vp[6 + dvp] * dp[13]) + (vp[5 + dvp] * dp[14]) + (vp[4 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples4() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[4 + dvp] * dp[0]) + (vp[3 + dvp] * dp[1]) + (vp[2 + dvp] * dp[2]) + (vp[1 + dvp] * dp[3]) + (vp[0 + dvp] * dp[4]) + (vp[15 + dvp] * dp[5])
                    + (vp[14 + dvp] * dp[6]) + (vp[13 + dvp] * dp[7]) + (vp[12 + dvp] * dp[8]) + (vp[11 + dvp] * dp[9]) + (vp[10 + dvp] * dp[10]) + (vp[9 + dvp] * dp[11])
                    + (vp[8 + dvp] * dp[12]) + (vp[7 + dvp] * dp[13]) + (vp[6 + dvp] * dp[14]) + (vp[5 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples5() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[5 + dvp] * dp[0]) + (vp[4 + dvp] * dp[1]) + (vp[3 + dvp] * dp[2]) + (vp[2 + dvp] * dp[3]) + (vp[1 + dvp] * dp[4]) + (vp[0 + dvp] * dp[5])
                    + (vp[15 + dvp] * dp[6]) + (vp[14 + dvp] * dp[7]) + (vp[13 + dvp] * dp[8]) + (vp[12 + dvp] * dp[9]) + (vp[11 + dvp] * dp[10]) + (vp[10 + dvp] * dp[11])
                    + (vp[9 + dvp] * dp[12]) + (vp[8 + dvp] * dp[13]) + (vp[7 + dvp] * dp[14]) + (vp[6 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples6() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[6 + dvp] * dp[0]) + (vp[5 + dvp] * dp[1]) + (vp[4 + dvp] * dp[2]) + (vp[3 + dvp] * dp[3]) + (vp[2 + dvp] * dp[4]) + (vp[1 + dvp] * dp[5])
                    + (vp[0 + dvp] * dp[6]) + (vp[15 + dvp] * dp[7]) + (vp[14 + dvp] * dp[8]) + (vp[13 + dvp] * dp[9]) + (vp[12 + dvp] * dp[10]) + (vp[11 + dvp] * dp[11])
                    + (vp[10 + dvp] * dp[12]) + (vp[9 + dvp] * dp[13]) + (vp[8 + dvp] * dp[14]) + (vp[7 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples7() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[7 + dvp] * dp[0]) + (vp[6 + dvp] * dp[1]) + (vp[5 + dvp] * dp[2]) + (vp[4 + dvp] * dp[3]) + (vp[3 + dvp] * dp[4]) + (vp[2 + dvp] * dp[5])
                    + (vp[1 + dvp] * dp[6]) + (vp[0 + dvp] * dp[7]) + (vp[15 + dvp] * dp[8]) + (vp[14 + dvp] * dp[9]) + (vp[13 + dvp] * dp[10]) + (vp[12 + dvp] * dp[11])
                    + (vp[11 + dvp] * dp[12]) + (vp[10 + dvp] * dp[13]) + (vp[9 + dvp] * dp[14]) + (vp[8 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples8() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[8 + dvp] * dp[0]) + (vp[7 + dvp] * dp[1]) + (vp[6 + dvp] * dp[2]) + (vp[5 + dvp] * dp[3]) + (vp[4 + dvp] * dp[4]) + (vp[3 + dvp] * dp[5])
                    + (vp[2 + dvp] * dp[6]) + (vp[1 + dvp] * dp[7]) + (vp[0 + dvp] * dp[8]) + (vp[15 + dvp] * dp[9]) + (vp[14 + dvp] * dp[10]) + (vp[13 + dvp] * dp[11])
                    + (vp[12 + dvp] * dp[12]) + (vp[11 + dvp] * dp[13]) + (vp[10 + dvp] * dp[14]) + (vp[9 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples9() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[9 + dvp] * dp[0]) + (vp[8 + dvp] * dp[1]) + (vp[7 + dvp] * dp[2]) + (vp[6 + dvp] * dp[3]) + (vp[5 + dvp] * dp[4]) + (vp[4 + dvp] * dp[5])
                    + (vp[3 + dvp] * dp[6]) + (vp[2 + dvp] * dp[7]) + (vp[1 + dvp] * dp[8]) + (vp[0 + dvp] * dp[9]) + (vp[15 + dvp] * dp[10]) + (vp[14 + dvp] * dp[11])
                    + (vp[13 + dvp] * dp[12]) + (vp[12 + dvp] * dp[13]) + (vp[11 + dvp] * dp[14]) + (vp[10 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples10() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[10 + dvp] * dp[0]) + (vp[9 + dvp] * dp[1]) + (vp[8 + dvp] * dp[2]) + (vp[7 + dvp] * dp[3]) + (vp[6 + dvp] * dp[4]) + (vp[5 + dvp] * dp[5])
                    + (vp[4 + dvp] * dp[6]) + (vp[3 + dvp] * dp[7]) + (vp[2 + dvp] * dp[8]) + (vp[1 + dvp] * dp[9]) + (vp[0 + dvp] * dp[10]) + (vp[15 + dvp] * dp[11])
                    + (vp[14 + dvp] * dp[12]) + (vp[13 + dvp] * dp[13]) + (vp[12 + dvp] * dp[14]) + (vp[11 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples11() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[11 + dvp] * dp[0]) + (vp[10 + dvp] * dp[1]) + (vp[9 + dvp] * dp[2]) + (vp[8 + dvp] * dp[3]) + (vp[7 + dvp] * dp[4]) + (vp[6 + dvp] * dp[5])
                    + (vp[5 + dvp] * dp[6]) + (vp[4 + dvp] * dp[7]) + (vp[3 + dvp] * dp[8]) + (vp[2 + dvp] * dp[9]) + (vp[1 + dvp] * dp[10]) + (vp[0 + dvp] * dp[11])
                    + (vp[15 + dvp] * dp[12]) + (vp[14 + dvp] * dp[13]) + (vp[13 + dvp] * dp[14]) + (vp[12 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples12() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[12 + dvp] * dp[0]) + (vp[11 + dvp] * dp[1]) + (vp[10 + dvp] * dp[2]) + (vp[9 + dvp] * dp[3]) + (vp[8 + dvp] * dp[4]) + (vp[7 + dvp] * dp[5])
                    + (vp[6 + dvp] * dp[6]) + (vp[5 + dvp] * dp[7]) + (vp[4 + dvp] * dp[8]) + (vp[3 + dvp] * dp[9]) + (vp[2 + dvp] * dp[10]) + (vp[1 + dvp] * dp[11])
                    + (vp[0 + dvp] * dp[12]) + (vp[15 + dvp] * dp[13]) + (vp[14 + dvp] * dp[14]) + (vp[13 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples13() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[13 + dvp] * dp[0]) + (vp[12 + dvp] * dp[1]) + (vp[11 + dvp] * dp[2]) + (vp[10 + dvp] * dp[3]) + (vp[9 + dvp] * dp[4]) + (vp[8 + dvp] * dp[5])
                    + (vp[7 + dvp] * dp[6]) + (vp[6 + dvp] * dp[7]) + (vp[5 + dvp] * dp[8]) + (vp[4 + dvp] * dp[9]) + (vp[3 + dvp] * dp[10]) + (vp[2 + dvp] * dp[11])
                    + (vp[1 + dvp] * dp[12]) + (vp[0 + dvp] * dp[13]) + (vp[15 + dvp] * dp[14]) + (vp[14 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples14() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[14 + dvp] * dp[0]) + (vp[13 + dvp] * dp[1]) + (vp[12 + dvp] * dp[2]) + (vp[11 + dvp] * dp[3]) + (vp[10 + dvp] * dp[4]) + (vp[9 + dvp] * dp[5])
                    + (vp[8 + dvp] * dp[6]) + (vp[7 + dvp] * dp[7]) + (vp[6 + dvp] * dp[8]) + (vp[5 + dvp] * dp[9]) + (vp[4 + dvp] * dp[10]) + (vp[3 + dvp] * dp[11])
                    + (vp[2 + dvp] * dp[12]) + (vp[1 + dvp] * dp[13]) + (vp[0 + dvp] * dp[14]) + (vp[15 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples15() {
        final double[] vp = actualV;
        final double[] tmpOut = tmpOutBuffer;
        int dvp = 0;
        for (int i = 0; i < 32; i++) {
            final double[] dp = D16[i];
            double pcmSample = (((vp[15 + dvp] * dp[0]) + (vp[14 + dvp] * dp[1]) + (vp[13 + dvp] * dp[2]) + (vp[12 + dvp] * dp[3]) + (vp[11 + dvp] * dp[4])
                    + (vp[10 + dvp] * dp[5]) + (vp[9 + dvp] * dp[6]) + (vp[8 + dvp] * dp[7]) + (vp[7 + dvp] * dp[8]) + (vp[6 + dvp] * dp[9]) + (vp[5 + dvp] * dp[10])
                    + (vp[4 + dvp] * dp[11]) + (vp[3 + dvp] * dp[12]) + (vp[2 + dvp] * dp[13]) + (vp[1 + dvp] * dp[14]) + (vp[0 + dvp] * dp[15])) * scaleFactor);
            tmpOut[i] = pcmSample;
            dvp += 16;
        }
    }

    private void computePcmSamples() {
        switch (actualWritePos) {
        case 0:
            computePcmSamples0();
            break;
        case 1:
            computePcmSamples1();
            break;
        case 2:
            computePcmSamples2();
            break;
        case 3:
            computePcmSamples3();
            break;
        case 4:
            computePcmSamples4();
            break;
        case 5:
            computePcmSamples5();
            break;
        case 6:
            computePcmSamples6();
            break;
        case 7:
            computePcmSamples7();
            break;
        case 8:
            computePcmSamples8();
            break;
        case 9:
            computePcmSamples9();
            break;
        case 10:
            computePcmSamples10();
            break;
        case 11:
            computePcmSamples11();
            break;
        case 12:
            computePcmSamples12();
            break;
        case 13:
            computePcmSamples13();
            break;
        case 14:
            computePcmSamples14();
            break;
        case 15:
            computePcmSamples15();
            break;
        default:
        }
    }

    /**
     * Calculate 32 PCM samples and write them
     */
    void calculatePcmSamples(double[] s, Decoder player) {
        for (int i = 0; i < 32; i++) {
            samples[i] = s[i];
        }
        computeNewV();
        computePcmSamples();
        player.appendSamples(channel, tmpOutBuffer);
        actualWritePos = (actualWritePos + 1) & 0xf;
        actualV = (actualV == v1) ? v2 : v1;
    }

}
